﻿namespace Foundation.Core
{
    public class SelectFilterRegistrar
    {
        public static readonly SelectFilterRegistrar Default = new SelectFilterRegistrar();

        private List<ITypeSelectCreateExecutor> _createExecutors = new List<ITypeSelectCreateExecutor>();
        private List<ISelectResultFilter> _resultFilter = new List<ISelectResultFilter>();

        private List<Func<Type, bool>> _typeAllowFilters = new List<Func<Type, bool>>();
        private Dictionary<Type, Func<Type, string>> _typeSelectNameFactories = new Dictionary<Type, Func<Type, string>>();

        public SelectFilterRegistrar()
        {
            _createExecutors.Add(new EnumSelectCreateExecutor());
            _createExecutors.Add(new ConstantSelectCreateExecutor());
        }

        public void AddExecutor<T>(T filter)
            where T : ITypeSelectCreateExecutor
        {
            _createExecutors.Add(filter);
        }

        public void AddResultFilter<T>(T filter)
            where T : ISelectResultFilter
        {
            _resultFilter.Add(filter);
        }

        public void AddTypeKeyFactory(Type type, Func<Type, string> factory) => _typeSelectNameFactories[type] = factory;

        public void AddTypeAllowFilter(Func<Type, bool> fatory) => _typeAllowFilters.Add(fatory);

        public IEnumerable<ITypeSelectCreateExecutor> GetExecutors(SelectCategory category)
        {
            var filters = _createExecutors;
            for (int i = filters.Count - 1; i > -1; i--)
            {
                var filter = filters[i];
                if (category == SelectCategory.Enum && filter is IEnumSelectFilterMetadata) yield return filter;
                else if (category == SelectCategory.Constant && filter is IConstantSelectFilterMetadata) yield return filter;
                else if (category == SelectCategory.Custom && filter is ICustomSelectFilterMetadata) yield return filter;
            }
        }

        public IEnumerable<ISelectResultFilter> GetResultFilters(SelectCategory category)
        {
            var filters = _resultFilter;
            for (int i = 0; i < filters.Count; i++)
            {
                var filter = filters[i];
                if (category == SelectCategory.Enum && filter is IEnumSelectFilterMetadata) yield return filter;
                else if (category == SelectCategory.Constant && filter is IConstantSelectFilterMetadata) yield return filter;
                else if (category == SelectCategory.Custom && filter is ICustomSelectFilterMetadata) yield return filter;
            }
        }

        public Func<Type, string> FinTypeNameFactory(Type type)
        {
            _typeSelectNameFactories.TryGetValue(type, out var factory);

            return factory;
        }

        public IList<Func<Type, bool>> GetAllowFilters() => _typeAllowFilters;
    }
}
